package com.oem.qisda;

import static com.oem.qisda.Constants.*;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

public class MainActivity extends AppCompatActivity {

    private boolean mReceiveThreadRunning = true;
    private boolean mReceiveThreadRunning2 = true;
    private static DatagramSocket socket;
    private static DatagramSocket cmdSocket;
    private static final int RECEIVEPORT = 10001;
    private static final int CMDPORT = 10002;
    private static final int SENDPORT = 10000;
    private byte[] mReceive = new byte[32];
    private byte[] mReceive2 = new byte[32];

    private static final String SERVERADDRESS = "127.0.0.1";
    private int mCommandGuardInerval = 50; //ms
    public static final String TAG = "UARTTreadMill";

    TreadmillAPI treadmillAPI = new TreadmillAPI();
    int APK_STATE = 0;
    int UCB_STATE = 0;

    private static final int INIT_INTERVAL = 1000; // 重试间隔1秒
    private static final int INIT_TIMEOUT = 5000;  // 超时时间5秒
    private Handler initHandler = new Handler(Looper.getMainLooper());
    private boolean initReceived = true; // 标记是否收到Init回复

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 获取 TextView 和 Button
        TextView resultTextView = findViewById(R.id.result_text_view);
        TextView ShowTextView = findViewById(R.id.show_text_view);

        // 定义多种按钮，绑定发送不同的指令事件，便于发送指令，用于调试，可删除
        // 按钮绑定
        Button buttonSet0 = findViewById(R.id.button_set0);
        Button buttonSet1 = findViewById(R.id.button_set1);
        Button buttonSet2 = findViewById(R.id.button_set2);
        Button buttonSet3 = findViewById(R.id.button_set3);
        Button buttonSet4 = findViewById(R.id.button_set4);
        Button buttonSet5 = findViewById(R.id.button_set5);

        // 设置按钮点击事件
        buttonSet0.setOnClickListener(view -> sendCommand(hexStringToByteArray(treadmillAPI.settingCommand(0))));
        buttonSet1.setOnClickListener(view -> sendCommand(hexStringToByteArray(treadmillAPI.settingCommand(1))));
        buttonSet2.setOnClickListener(view -> sendCommand(hexStringToByteArray(treadmillAPI.settingCommand(2))));
        buttonSet3.setOnClickListener(view -> sendCommand(hexStringToByteArray(treadmillAPI.settingCommand(3))));
        buttonSet4.setOnClickListener(view -> sendCommand(hexStringToByteArray(treadmillAPI.settingCommand(4))));
        buttonSet5.setOnClickListener(view -> sendCommand(hexStringToByteArray(treadmillAPI.settingCommand(5))));

        Button buttonInit = findViewById(R.id.button_init);
        Button buttonReset = findViewById(R.id.button_reset);
        Button buttonSetInc = findViewById(R.id.button_setInc);
        Button buttonCalibrate = findViewById(R.id.button_calibrate);
        Button buttonDiagnostics = findViewById(R.id.button_diagnostics);

        buttonInit.setOnClickListener(view -> sendCommand(hexStringToByteArray(treadmillAPI.initCommand())));
        buttonReset.setOnClickListener(view -> sendCommand(hexStringToByteArray(treadmillAPI.resetCommand())));
        buttonSetInc.setOnClickListener(view -> sendCommand(hexStringToByteArray(treadmillAPI.setInclineCommand(10.0f))));
        buttonCalibrate.setOnClickListener(view -> sendCommand(hexStringToByteArray(treadmillAPI.calibrateCommand())));
        buttonDiagnostics.setOnClickListener(view -> sendCommand(hexStringToByteArray(treadmillAPI.diagnosticsCommand(5))));

        Button buttonStateChangeACK = findViewById(R.id.button_stateChangeACK);
        buttonStateChangeACK.setOnClickListener(view -> sendCommand(hexStringToByteArray(treadmillAPI.stateChangedCommand())));
        Button buttonGoScreen = findViewById(R.id.button_go_on_screen);
        buttonGoScreen.setOnClickListener(view -> {
            if (APK_STATE == APK_STATE_READY) {
                sendCommand(hexStringToByteArray(treadmillAPI.settingCommand(2)));
            }
        });

        // 打印so库中的每种生成的指令，用于调试，可删除
        Log.d(TAG, treadmillAPI.initCommand());
        Log.d(TAG, treadmillAPI.settingCommand(0));
        Log.d(TAG, treadmillAPI.resetCommand());
        Log.d(TAG, treadmillAPI.setInclineCommand(12.0f));
        Log.d(TAG, treadmillAPI.calibrateCommand());
        Log.d(TAG, treadmillAPI.diagnosticsCommand(5));
        Log.d(TAG, treadmillAPI.stateChangedCommand());
        Log.d(TAG, treadmillAPI.errorDataCommand());

        // APK启动后就发送init指令
        sendInitCommandWithRetry();

        // 打印每种指令处理之后的返回的json，用于调试，可删除
        Log.d(TAG, "\n");
        Log.d(TAG, treadmillAPI.parseCommand("010A000101010D"));
        Log.d(TAG, treadmillAPI.parseCommand("010B000000000B"));
        Log.d(TAG, treadmillAPI.parseCommand("010C000000000C"));
        Log.d(TAG, treadmillAPI.parseCommand("010D000000000D"));
        Log.d(TAG, treadmillAPI.parseCommand("011A020000001C"));
        Log.d(TAG, treadmillAPI.parseCommand("011B006400007F"));
        Log.d(TAG, treadmillAPI.parseCommand("01A00164320138"));
        Log.d(TAG, treadmillAPI.parseCommand("01A101000000A2"));
        Log.d(TAG, treadmillAPI.parseCommand("01E004513C0071"));

        // 启动接收线程
        try {
            socket = new DatagramSocket(RECEIVEPORT);
        } catch (SocketException e) {
            e.printStackTrace();
            Log.e(TAG, "Socket 初始化失败");
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
                final DatagramPacket dPacket = new DatagramPacket(mReceive, mReceive.length);
                while (mReceiveThreadRunning) {
                    try {
                        // 接收数据
                        socket.receive(dPacket);
                        int receiveSize = dPacket.getLength();
                        String command = bytesToHex(mReceive, receiveSize);
                        Log.d(TAG, "收到命令: " + command);

                        // 解析接收到的命令
                        String type = command.substring(2, 4);
                        String result = treadmillAPI.parseCommand(command);
                        UCB_STATE = treadmillAPI.getState();

                        try {
                            // 将 JSON 字符串解析为 JSONObject
                            JSONObject dataObject = new JSONObject(result);

                            switch (type) {
                                case COMMAND_INIT:
                                    if (!initReceived) {
                                        // 此时正在进行第一次初始化，需要查看重传机制是否生效
                                        handleResponse(command);
                                    }
                                    break;
                                case COMMAND_CALIBRATE:
                                    int progress = Integer.parseInt(dataObject.getString("progress"));
                                    String treadmillProgress = getProgress(progress);
                                    Log.d(TAG, "Calibrate: " + treadmillProgress);
                                    break;
                                case COMMAND_DIAGNOSTICS:
                                    int option = Integer.parseInt(dataObject.getString("option"));
                                    int progressDia = Integer.parseInt(dataObject.getString("progress"));
                                    int testData = Integer.parseInt(dataObject.getString("testData"));
                                    handleDiagnostics(option, progressDia, testData);
                                    break;
                                case COMMAND_WORKOUT_DATA:
                                    Log.d(TAG, "state: " + dataObject.getString("state"));
                                    Log.d(TAG, "speed: " + dataObject.getString("speed"));
                                    Log.d(TAG, "speedUnit: " + dataObject.getString("speedUnit"));
                                    Log.d(TAG, "incline: " + dataObject.getString("incline"));
                                    Log.d(TAG, "isPersonDetected: " + dataObject.getString("isPersonDetected"));
                                    break;
                                case COMMAND_STATE_CHANGED:
                                    sendCommand(hexStringToByteArray(treadmillAPI.stateChangedCommand()));
                                    if (UCB_STATE == 2 && APK_STATE == APK_STATE_READY) {
                                        while (treadmillAPI.getState() != 1) {
                                            sendCommand(hexStringToByteArray(treadmillAPI.settingCommand(2)));
                                            if(treadmillAPI.getState() != 1)
                                                Thread.sleep(1000);
                                        }
                                        APK_STATE = APK_STATE_RUNNING;
                                    } else if (UCB_STATE == 1) {
                                        APK_STATE = APK_STATE_RUNNING;
                                    } else if (UCB_STATE == 5 && APK_STATE == APK_STATE_RUNNING) {
                                        Log.d(TAG, "wait for stopping");
                                    } else if (UCB_STATE == 3) {
                                        APK_STATE = APK_STATE_PAUSED;
                                    } else if (UCB_STATE == 0) {
                                        APK_STATE = APK_STATE_READY;
                                    } else if (UCB_STATE == 4) {
                                        APK_STATE = APK_STATE_ERROR;
                                    }
                                    String UCBstate = dataObject.getString("state");
                                    Log.d(TAG, "UCB State is " + UCBstate);
                                    break;
                                case COMMAND_ERROR:
                                    sendCommand(hexStringToByteArray(treadmillAPI.errorDataCommand()));
                                    int errorCode = Integer.parseInt(dataObject.getString("code"));
                                    String errorMessage = getErrorMessage(errorCode);
                                    Log.d(TAG, errorMessage);
                                    break;
                                default:
                                    String message = dataObject.getString("message");
                                    Log.d(TAG, message);
                            }
                        } catch (Exception e) {
                            Log.e(TAG, "JSON parsing error: " + e.getMessage());
                        }

                        // 在UI线程中更新结果显示
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                resultTextView.setText("解析结果: " + result);
                                Toast.makeText(MainActivity.this, "解析完成：" + result, Toast.LENGTH_SHORT).show();
                            }
                        });

                    } catch (IOException e) {
                        Log.e(TAG, "接收数据时出错");
                        e.printStackTrace();
                    }
                    Log.d(TAG, "\n");
                }
            }
        }).start();

        try {
            cmdSocket = new DatagramSocket(CMDPORT);
        } catch (SocketException e) {
            throw new RuntimeException(e);
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
                final DatagramPacket dPacket = new DatagramPacket(mReceive2, 32);
                while (mReceiveThreadRunning2) {
                    try {
                        cmdSocket.receive(dPacket);
                    } catch (IOException e) {
                        Log.d(TAG, "Send Packet: failed");
                        e.printStackTrace();
                    }
                    int receiveSize = dPacket.getLength();
                    StringBuilder sb = new StringBuilder();
                    sb.append("Send Packet: ");
                    for (int i = 0; i < receiveSize - 2; i++) {
                        sb.append(String.format("%02x", mReceive2[i]).toUpperCase());
                    }
                    Log.d(TAG, sb.toString());
                    Log.d(TAG, "\n");
                }
            }
        }).start();

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mReceiveThreadRunning = false;
        mReceiveThreadRunning2 = false;
        if (socket != null && !socket.isClosed()) {
            socket.close();
        }
        if (cmdSocket != null && !cmdSocket.isClosed()) {
            cmdSocket.close();
        }
    }

    public static String bytesToHex(byte[] bytes, int len) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < len; i++) {
            sb.append(String.format("%02X", bytes[i]));
        }
        return sb.toString().trim();
    }

    private void sendCommand(final byte[] value) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 初始化socket
                DatagramSocket socket = null;
                try {
                    socket = new DatagramSocket();
                } catch (SocketException e) {
                    e.printStackTrace();
                }

                InetAddress serverAddress = null;
                try {
                    serverAddress = InetAddress.getByName(SERVERADDRESS);
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }

                // 创建一个包含命令数据、服务器地址和端口号的数据包
                DatagramPacket packet = new DatagramPacket(value, value.length, serverAddress, SENDPORT);
                try {
                    // 将UDP数据包发送到中间件
                    socket.send(packet);
                    Log.d(TAG, "send success!");
                    Log.d(TAG, "packet length: " + packet.getLength());
                } catch (IOException e) {
                    Log.d(TAG, "send failed!");
                    e.printStackTrace();
                }
                socket.close();

                // 等待指定的命令保护间隔，以免发送过多命令
                try {
                    Thread.sleep(mCommandGuardInerval);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    // 将十六进制字符串转换为字节数组
    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i + 1), 16));
        }
        return data;
    }

    private void sendInitCommandWithRetry() {
        initReceived = false; // 重置标志
        sendCommand(hexStringToByteArray(treadmillAPI.initCommand())); // 初次发送Init指令

        initHandler.postDelayed(new Runnable() {
            private int elapsedTime = 0;

            @Override
            public void run() {
                if (initReceived) {
                    Log.d(TAG, "Init Success");
                    return; // 成功收到回复，停止重试
                }

                if (elapsedTime >= INIT_TIMEOUT) {
                    Log.d(TAG, "Init failed"); // 超过5秒未收到响应，打印失败日志
                    return; // 超时，停止重试
                } else {
                    sendCommand(hexStringToByteArray(treadmillAPI.initCommand()));
                    elapsedTime += INIT_INTERVAL;
                }

                // 继续重试
                initHandler.postDelayed(this, INIT_INTERVAL);
            }
        }, INIT_INTERVAL);
    }

    private void handleResponse(String response) {
        // 解析响应内容并判断是否为Init响应
        if (response.startsWith("010A")) {
            initReceived = true;
            APK_STATE = APK_STATE_READY;
            Log.d(TAG, "Init Success");
        }
    }

    public String getProgress(int progress) {
        String status;

        if (progress == 0) {
            status = "started";
        } else if (progress == 1) {
            status = "incline going up";
        } else if (progress == 2) {
            status = "incline going down";
        } else if (progress == 3) {
            status = "finished";
        } else if (progress == 4) {
            status = "error";
        } else {
            status = "unknown progress";
        }
        return status;
    }

    public String getErrorMessage(int errorCode) {
        String message;

        switch (errorCode) {
            case 3:
                message = "Safety key activated";
                break;
            case 81:
                message = "No speed sensor signal";
                break;
            case 82:
                message = "Low speed error (Speed sensor connector or cable damaged)";
                break;
            case 5:
                message = "Incline is not moved or signal lost for more than 5 seconds";
                break;
            case 51:
                message = "Need calibration – incline motor calibration should be done. For example, when you change MCB.";
                break;
            case 4:
                message = "Motor Run-Away protection";
                break;
            case 40:
                message = "Over acceleration (0.8KPH~ 7.9KPH)";
                break;
            case 41:
                message = "Over acceleration at starting (0KPH~0.8KPH)";
                break;
            case 83:
                message = "High speed error (8KPH~19.2KPH)";
                break;
            default:
                message = "Other error";
                break;
        }
        return message;
    }

    public void handleDiagnostics(int option, int progressDia, int testData) {
        Log.d(TAG, "option is " + option);
        Log.d(TAG, "progressDia is " + progressDia);
        Log.d(TAG, "testData is " + testData);

        // 处理选项
        switch (option) {
            case 5:
                if (progressDia == 0) {
                    Log.d(TAG, "Incline test started, incline value: " + testData * 0.1 + "%");
                } else if (progressDia == 3) {
                    Log.d(TAG, "Incline test finished, incline value: " + testData * 0.1 + "%");
                }
                break;

            case 81:
            case 82:
                if (progressDia == 0) {
                    Log.d(TAG, "Speed sensor test started, speed value: " + (testData / 10.0) + " KPH");
                } else if (progressDia == 3) {
                    Log.d(TAG, "Speed sensor test finished, speed value: " + (testData / 10.0) + " KPH");
                }
                break;

            default:
                Log.d(TAG, "Unknown option: " + option);
                break;
        }
    }

}